package com.lightsaber.trade.cloth.rest.resources;

import java.util.GregorianCalendar;
import java.util.List;

import javax.xml.datatype.DatatypeFactory;

import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springside.modules.orm.Page;
import org.springside.modules.utils.spring.SpringContextHolder;

import com.lightsaber.trade.cloth.dao.planning.Level2BreakDownDao;
import com.lightsaber.trade.cloth.dao.planning.Level2DisciplineStatusDao;
import com.lightsaber.trade.cloth.dao.planning.Level2PlanningDao;
import com.lightsaber.trade.cloth.dao.planning.PlanningDao;
import com.lightsaber.trade.cloth.dao.programme.CurriculumHourDao;
import com.lightsaber.trade.cloth.dao.programme.OfferingCampusDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammeDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammeLeaderDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammePlanProjectionTypeDao;
import com.lightsaber.trade.cloth.entity.planning.Level2BreakDown;
import com.lightsaber.trade.cloth.entity.planning.Level2DisciplineStatus;
import com.lightsaber.trade.cloth.entity.planning.Level2Planning;
import com.lightsaber.trade.cloth.entity.planning.ProgrammePlanningItem;
import com.lightsaber.trade.cloth.entity.programme.CurriculumHour;
import com.lightsaber.trade.cloth.entity.programme.OfferingCampus;
import com.lightsaber.trade.cloth.entity.programme.Programme;
import com.lightsaber.trade.cloth.entity.programme.ProgrammeLeader;
import com.lightsaber.trade.cloth.rest.data.CurriculumHourType;
import com.lightsaber.trade.cloth.rest.data.OfferingCampusType;
import com.lightsaber.trade.cloth.rest.data.PlannedPlaceBreakdownType;
import com.lightsaber.trade.cloth.rest.data.ProgrammeDetails;
import com.lightsaber.trade.cloth.rest.data.ProgrammeLeaderType;
import com.lightsaber.trade.cloth.rest.data.ProgrammePlanProjectionType;
import com.lightsaber.trade.cloth.rest.data.ProgrammePlannedPlaces;

@Component
public class RestfulHppCore {
    /*** Common ***/
    private final static Logger log = LoggerFactory.getLogger(RestfulHppCore.class);
    private OpenSessionInRestfulFilter openSessionInRestfulFilter = SpringContextHolder
            .getBean("openSessionInRestfulFilter");
    private boolean processSucess = true;

    /*** getProgrammeDetails ***/
    private ProgrammeDetails details = new ProgrammeDetails();
    private Programme programme = new Programme();

    private ProgrammePlanProjectionType programmePlanProjectionType = new ProgrammePlanProjectionType();
    private OfferingCampusType offeringCampusType = new OfferingCampusType();
    private CurriculumHourType curriculumHourType = new CurriculumHourType();
    private ProgrammeLeaderType programmeLeaderType = new ProgrammeLeaderType();

    private ProgrammeDao programmeDao = new ProgrammeDao();
    private ProgrammePlanProjectionTypeDao programmePlanProjectionTypeDao = new ProgrammePlanProjectionTypeDao();
    private OfferingCampusDao offeringCampusDao = new OfferingCampusDao();
    private CurriculumHourDao curriculumHourDao = new CurriculumHourDao();
    private ProgrammeLeaderDao programmeLeaderDao = new ProgrammeLeaderDao();

    /*** getProgrammePlannedPlaces ***/
    ProgrammePlannedPlaces places = new ProgrammePlannedPlaces();

    private Level2Planning level2Planning = new Level2Planning();
    private Level2BreakDown level2BreakDown = new Level2BreakDown();
    private Level2DisciplineStatus level2DisciplineStatus = new Level2DisciplineStatus();

    private PlannedPlaceBreakdownType plannedPlaceBreakdownType = new PlannedPlaceBreakdownType();

    private Level2PlanningDao level2PlanningDao = new Level2PlanningDao();
    private Level2DisciplineStatusDao level2DisciplineStatusDao = new Level2DisciplineStatusDao();
    private Level2BreakDownDao level2BreakDownDao = new Level2BreakDownDao();
    private PlanningDao planningDao = new PlanningDao();

    public static void main(final String arg[]) {
        log.error("main: START: ");
        RestfulHppCore rest = new RestfulHppCore();

        /* Test: getProgrammeDetails */
        int launchYear = 2011;
        String launchVersion = "M01";
        String programmeCode = "AA1002";

        rest.details = rest.getProgrammeDetails(launchYear, launchVersion, programmeCode);
        if (rest.processSucess == true) {
            log.error("details.getProgrammeTitleShort()=" + rest.details.getProgrammeTitleShort());
            log.error("details.getProgrammeTitleLong()=" + rest.details.getProgrammeTitleLong());
        } else {
            log.error("Process Failed");
        }

        /* Test: getProgrammePlannedPlaces */
        int planningExercise = 2011;
        int planYear = 2013;
        int yearOfStudy = 2;
        // String programmeCode = "AAA";
        programmeCode = "cherry2-2";
        // int launchYear = 2000;
        launchYear = 9999;
        // String launchVersion = "M01";
        launchVersion = "test";
        String operatingCampus = "1";
        String operatingDepartment = "1";

        rest.places = rest.getProgrammePlannedPlaces(planningExercise, planYear, yearOfStudy,
                programmeCode, launchYear, launchVersion, operatingCampus, operatingDepartment);
        if (rest.processSucess == true) {
            log.error("places.getPlannedPlacesNew()=" + rest.places.getPlannedPlacesNew());
            log.error("places.getPlannedPlacesProg()=" + rest.places.getPlannedPlacesProg());
            for (int i = 0; i < rest.places.getPlannedPlaceBreakdown().size(); i++) {
                log.error("places > getSemesterOrMonth()="
                        + rest.places.getPlannedPlaceBreakdown().get(i).getSemesterOrMonth());
                log.error("places > getPlannedPlace()="
                        + rest.places.getPlannedPlaceBreakdown().get(i).getPlannedPlace());
            }
        } else {
            log.error("Process Failed");
        }
    }

    public ProgrammeDetails getProgrammeDetails(final int launchYear, final String launchVersion,
            final String programmeCode) {
        programme.setLaunchYear(launchYear);
        programme.setLaunchVersion(launchVersion);
        programme.setCode(programmeCode);
        String[] execArgs = { "getProgrammeDetails" };
        execute(execArgs);
        return details;
    }

    public String prepareProgrammeDetails(final SessionFactory sess, final String[] args) {
        programmeDao.setSessionFactory(sess);
        programmePlanProjectionTypeDao.setSessionFactory(sess);
        offeringCampusDao.setSessionFactory(sess);
        curriculumHourDao.setSessionFactory(sess);
        programmeLeaderDao.setSessionFactory(sess);

        Page<Programme> programmePage = new Page<Programme>();
        programmePage.setPageSize(1);
        programmeDao.findPage(programme, programmePage);
        List<Programme> programmes = programmePage.getResult();

        if (programmes.size() > 0) {
            details.setProgrammeCode(programmes.get(0).getCode());
            // details.setFirstLaunchYear(programmes.get(0).getFirstLaunchYear());
            details.setLaunchYear(programmes.get(0).getLaunchYear());
            details.setLaunchVersion(programmes.get(0).getLaunchVersion());
            details.setProgrammeTitleShort(programmes.get(0).getTitleShort());
            details.setProgrammeTitleLong(programmes.get(0).getTitleLong());
            details.setProgrammeTitleChiShort(programmes.get(0).getTitleZhShort());
            details.setProgrammeTitleChiLong(programmes.get(0).getTitleZhLong());
            // details.setStreamOrProgramme(programmes.get(0).getStreamOrProgramme());
            details.setDisciplineFacultyOU(programmes.get(0).getDiscipline());
            details.setHighestAward(programmes.get(0).getAward());
            details.setProgrammeBoardOrAcademicOwner(programmes.get(0).getBoard());
            details.setProgrammeCategory(programmes.get(0).getPreEmploymentInService());
            details.setMainStream(programmes.get(0).getMainStream());
            details.setFundingMode(programmes.get(0).getFundingMode());
            details.setDeliveryModeOrStudyMode(programmes.get(0).getStudyMode());
            details.setLevelOfStudy(programmes.get(0).getStudylevel());
            // details.setBureau(programmes.get(0).getBureau());
            details.setMinEntryLevel(programmes.get(0).getMinimumnEntryLevel());
            details.setDuration(programmes.get(0).getDuration());
            // details.setIsSubjectGroup(programmes.get(0).isSubjectGroup());
            details.setQFLevel(programmes.get(0).getQfLevel());
            // details.setPAA(programmes.get(0).getPaa());
            // details.setSubPAA(programmes.get(0).getSubPaa());
            details.setGAR(programmes.get(0).getGarType());
            details.setHKCAAVQValidationPeriod(programmes.get(0).getCaavqValidationPeriod());
            details.setStudentCardExpiry(String.valueOf(programmes.get(0).getCardExpiry()));
            details.setProgrammeStatus(programmes.get(0).getStatus().toString()); // list
                                                                                  // out
                                                                                  // the
                                                                                  // possible
                                                                                  // out
            details.setProgrammeClassification(programmes.get(0).getClassification());
            details.setProgrammeType(programmes.get(0).getProgrammeType());

            log.debug(" > details.getProgrammeCode()=" + details.getProgrammeCode());
            log.debug(" > details.getFirstLaunchYear()=" + details.getFirstLaunchYear());
            log.debug(" > details.getLaunchYear()=" + details.getLaunchYear());
            log.debug(" > details.getLaunchVersion()=" + details.getLaunchVersion());
            log.debug(" > details.getProgrammeTitleShort()=" + details.getProgrammeTitleShort());
            log.debug(" > details.getProgrammeTitleLong()=" + details.getProgrammeTitleLong());
            log.debug(" > details.getProgrammeTitleChiShort()="
                    + details.getProgrammeTitleChiShort());
            log.debug(" > details.getProgrammeTitleChiLong()=" + details.getProgrammeTitleChiLong());
            log.debug(" > details.getStreamOrProgramme()=" + details.getStreamOrProgramme());
            log.debug(" > details.getDisciplineFacultyOU()=" + details.getDisciplineFacultyOU());
            log.debug(" > details.getHighestAward()=" + details.getHighestAward());
            log.debug(" > details.getProgrammeBoardOrAcademicOwner()="
                    + details.getProgrammeBoardOrAcademicOwner());
            log.debug(" > details.getProgrammeCategory()=" + details.getProgrammeCategory());
            log.debug(" > details.getMainStream()=" + details.getMainStream());
            log.debug(" > details.getFundingMode()=" + details.getFundingMode());
            log.debug(" > details.getDeliveryModeOrStudyMode()="
                    + details.getDeliveryModeOrStudyMode());
            log.debug(" > details.getLevelOfStudy()=" + details.getLevelOfStudy());
            log.debug(" > details.getBureau()=" + details.getBureau());
            log.debug(" > details.getMinEntryLevel()=" + details.getMinEntryLevel());
            log.debug(" > details.getDuration()=" + details.getDuration());
            log.debug(" > details.getIsSubjectGroup()=" + details.isIsSubjectGroup());
            log.debug(" > details.getQFLevel()=" + details.getQFLevel());
            log.debug(" > details.getRequiredProgrammePlanning()="
                    + details.isRequiredProgrammePlanning());
            log.debug(" > details.getPAA()=" + details.getPAA());
            log.debug(" > details.getSubPAA()=" + details.getSubPAA());
            log.debug(" > details.getGAR()=" + details.getGAR());
            log.debug(" > details.getHKCAAVQValidationPeriod()="
                    + details.getHKCAAVQValidationPeriod());
            log.debug(" > details.getStudentCardExpiry()=" + details.getStudentCardExpiry());
            log.debug(" > details.getProgrammeStatus()=" + details.getProgrammeStatus());
            log.debug(" > details.getProgrammeClassification()="
                    + details.getProgrammeClassification());
            log.debug(" > details.getProgrammeType()=" + details.getProgrammeType());

            // XMLGregorianCalendar to Date:
            // XMLGregorianCalendar xcal = <assume this is initialized>;
            // java.util.Date dt = xcal.toGregorianCalendar().getTime();
            GregorianCalendar cal = new GregorianCalendar();
            cal.setTime(programmes.get(0).getApprovalDate());
            try {
                details.setProgrammeApprovalDate(DatatypeFactory.newInstance()
                        .newXMLGregorianCalendar(cal));
            } catch (Exception e) {
                log.error("Exception in setProgrammeApprovalDate");
            }

            // <Entity> xxx = <Dao>.xxx(programmes.get(0).getXxx())
            // <restfulType>.setXxx(Entity.get(xx))
            List<com.lightsaber.trade.cloth.entity.programme.ProgrammePlanProjectionType> programmePlanProjectionTypes = programmePlanProjectionTypeDao
                    .findByProgramme(programmes.get(0));
            for (int i = 0; i < programmePlanProjectionTypes.size(); i++) {
                programmePlanProjectionType.setYear(String.valueOf(programmePlanProjectionTypes
                        .get(i).getYear()));
                programmePlanProjectionType.setInputType(programmePlanProjectionTypes.get(i)
                        .getPlanProjectionType().toString());
                programmePlanProjectionType.setSelfDefinedProgressionRate(String
                        .valueOf(programmePlanProjectionTypes.get(i)
                                .getSelfDefinedProgressionRate()));
                details.getProgrammePlanProjection().add(programmePlanProjectionType);
            }
            List<OfferingCampus> offeringCampuss = offeringCampusDao.findByProgramme(programmes
                    .get(0));
            for (int i = 0; i < offeringCampuss.size(); i++) {
                offeringCampusType.setOperatingCampus(offeringCampuss.get(i).getOperatingCampus());
                offeringCampusType.setOperatingUnit(offeringCampuss.get(i).getOperatingUnit());
                offeringCampusType.setOfferingCampus(offeringCampuss.get(i).getCampus());
                offeringCampusType.setOfferingUnit(offeringCampuss.get(i).getOfferingUnit());
                details.getOfferingCampus().add(offeringCampusType);
            }
            List<CurriculumHour> curriculumnHours = curriculumHourDao.findByProgrammeId(programmes
                    .get(0).getId());
            for (int i = 0; i < curriculumnHours.size(); i++) {
                curriculumHourType.setYear(String.valueOf(curriculumnHours.get(i).getYear()));
                curriculumHourType.setCurriculumHour(String.valueOf(curriculumnHours.get(i)
                        .getHours()));
                details.getCurriculumHours().add(curriculumHourType);
            }
            List<ProgrammeLeader> programmeLeaders = programmeLeaderDao
                    .findByProgrammeId(programmes.get(0).getId());
            for (int i = 0; i < programmeLeaders.size(); i++) {
                programmeLeaderType.setEnglishName(programmeLeaders.get(i).getEnglishName());
                programmeLeaderType.setChineseName(programmeLeaders.get(i).getChineseName());
                programmeLeaderType.setEmail(programmeLeaders.get(i).getEmail());
                programmeLeaderType.setIsLeader(programmeLeaders.get(i).isLeader());
                details.getProgrammeLeader().add(programmeLeaderType);
            }
            return null;
        }
        sess.getCurrentSession().flush();
        return " Record Not Found";
    }

    public ProgrammePlannedPlaces getProgrammePlannedPlaces(final int planningExercise,
            final int planYear, final int yearOfStudy, final String programmeCode,
            final int launchYear, final String launchVersion, final String operatingCampus,
            final String operatingDepartment) {

        // level2BreakDown
        // level2DisciplineStatus
        level2Planning.setPlanningExercise(planningExercise);

        String[] execArgs = { "getProgrammePlannedPlaces", String.valueOf(planningExercise),
                String.valueOf(planYear), String.valueOf(yearOfStudy), programmeCode,
                String.valueOf(launchYear), launchVersion, operatingCampus, operatingDepartment };
        execute(execArgs);
        return places;
    }

    public String prepareProgrammePlannedPlaces(final SessionFactory sess, final String[] args) {
        level2PlanningDao.setSessionFactory(sess);
        level2DisciplineStatusDao.setSessionFactory(sess);
        level2BreakDownDao.setSessionFactory(sess);
        planningDao.setSessionFactory(sess);

        Page<Level2Planning> level2PlanningPage = new Page<Level2Planning>();
        level2PlanningPage.setPageSize(1);
        level2PlanningDao.findPage(level2Planning, level2PlanningPage);
        List<Level2Planning> level2Plannings = level2PlanningPage.getResult();

        if (level2Plannings.size() > 0) {
            List<ProgrammePlanningItem> programmePlanningItems = level2Plannings.get(0)
                    .getProgrammePlanningItems();

            if (args.length < 9) {
                return " Not enough input arguments";
            }
            int reqPlanningExercise = Integer.parseInt(args[1]);
            int reqPlanYear = Integer.parseInt(args[2]);
            int reqYearOfStudy = Integer.parseInt(args[3]);
            String reqProgrammeCode = args[4];
            int reqLaunchYear = Integer.parseInt(args[5]);
            String reqLaunchVersion = args[6];
            String reqOperatingCampus = args[7];
            String reqOperatingDepartment = args[8];

            log.debug(" > reqPlanningExercise=" + reqPlanningExercise);
            log.debug(" > reqPlanYear=" + reqPlanYear);
            log.debug(" > reqYearOfStudy=" + reqYearOfStudy);
            log.debug(" > reqProgrammeCode=" + reqProgrammeCode);
            log.debug(" > reqLaunchYear=" + reqLaunchYear);
            log.debug(" > reqLaunchVersion=" + reqLaunchVersion);
            log.debug(" > reqOperatingCampus=" + reqOperatingCampus);
            log.debug(" > reqOperatingDepartment=" + reqOperatingDepartment);

            for (int i = 0; i < programmePlanningItems.size(); i++) {
                // reqLaunchYear == ?? missing in db
                // reqLaunchVersion.equalsIgnoreCase(??) missing in db
                // if( reqPlanYear ==
                // programmePlanningItems.get(i).getPlanYear() &&
                // reqYearOfStudy ==
                // programmePlanningItems.get(i).getStudyYear() &&
                // reqProgrammeCode.equalsIgnoreCase(programmePlanningItems.get(i).getCode())
                // &&
                // reqOperatingCampus.equalsIgnoreCase(programmePlanningItems.get(i).getOperatingCampus())
                // &&
                // reqOperatingDepartment.equalsIgnoreCase(programmePlanningItems.get(i).getOperatingUnit())
                // )
                {
                    places.setPlannedPlacesNew(programmePlanningItems.get(i).getPlannedPlacesNew());
                    places.setPlannedPlacesProg(programmePlanningItems.get(i)
                            .getPlannedPlacesProg());
                    log.debug(" -----> getPlannedPlacesNew" + places.getPlannedPlacesNew());
                    log.debug(" -----> getPlannedPlacesProg" + places.getPlannedPlacesProg());
                    places.setPlanningExercise(reqPlanningExercise);
                    places.setPlanYear(reqPlanYear);
                    places.setYearOfStudy(reqYearOfStudy);
                    places.setProgrammeCode(reqProgrammeCode);
                    places.setLaunchYear(reqLaunchYear);
                    places.setLaunchVersion(reqLaunchVersion);
                    places.setOperatingCampus(reqOperatingCampus);
                    places.setOperatingDepartment(reqOperatingDepartment);

                    Page<Level2BreakDown> level2BreakDownPage = new Page<Level2BreakDown>();
                    level2BreakDownPage.setPageSize(1);
                    List<Level2BreakDown> level2BreakDowns = level2BreakDownDao
                            .findByProgrammePlanningItemId(programmePlanningItems.get(i).getId());
                    for (int j = 0; j < level2BreakDowns.size(); j++) {
                        plannedPlaceBreakdownType.setPlannedPlace(level2BreakDowns.get(j)
                                .getPlannedPlaces());
                        plannedPlaceBreakdownType.setSemesterOrMonth(level2BreakDowns.get(j)
                                .getSemester());
                        places.getPlannedPlaceBreakdown().add(plannedPlaceBreakdownType);
                    }
                }// end of > prepared matched record
            }// end of > matching process for programmePlanningItems
        }// end of > if (level2Plannings.size()>0)
        else {
            return " Record not found";
        }
        return null;
    }

    // manage session/transaction
    public void execute(final String[] args) {
        log.error("To open session.");
        // args will be passed to doExecute(args)
        openSessionInRestfulFilter.doFilterInternal(this, args);

    }

    public void doExecute(final SessionFactory sess, final String[] args) {
        if (args != null && args.length > 0) {
            if (args[0].equalsIgnoreCase("getProgrammeDetails")) {

                String errMsg = prepareProgrammeDetails(sess, args);
                if (errMsg != null) {
                    processSucess = false;
                    log.error(errMsg);
                }
            } else if (args[0].equalsIgnoreCase("getProgrammePlannedPlaces")) {

                String errMsg = prepareProgrammePlannedPlaces(sess, args);
                if (errMsg != null) {
                    processSucess = false;
                    log.error(errMsg);
                }
            } else {
                processSucess = false;
                log.error(" Requested REST service is not available: " + args[0]);
            }

        } else {
            processSucess = false;
            log.error(" args missing");
        }
    }

}
